package com.alibaba.wallstreet.uitls;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;

import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle.util.io.Streams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Taken from org.bouncycastle.openpgp.examples
 * 
 * @author seamans
 */
public class PGPUtil {

    private static Logger logger = LoggerFactory.getLogger(PGPUtil.class);

    private static final int   BUFFER_SIZE                    = 1 << 11;  // should always be power of 2 设置的是1024
    
    private static final int   KEY_FLAGS                      = 27;
    
    private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[] { 
                                                                            PGPSignature.POSITIVE_CERTIFICATION,
                                                                            PGPSignature.CASUAL_CERTIFICATION, 
                                                                            PGPSignature.NO_CERTIFICATION, 
                                                                            PGPSignature.DEFAULT_CERTIFICATION
                                                                          };
    
    @SuppressWarnings("unchecked")
    public static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
        in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);

        PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);

        //
        // we just loop through the collection till we find a key suitable for encryption, in the real
        // world you would probably want to be a bit smarter about this.
        //
        PGPPublicKey key = null;

        //
        // iterate through the key rings.
        //
        Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();
        while (key == null && rIt.hasNext()) {
            PGPPublicKeyRing kRing = rIt.next();
            Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
            while (key == null && kIt.hasNext()) {
                PGPPublicKey k = kIt.next();
                if (k.isEncryptionKey()) {
                    key = k;
                }
            }
        }

        if (key == null) {
            throw new IllegalArgumentException("Can't find encryption key in key ring.");
        }

        return key;
    }

    
    @SuppressWarnings("unchecked")
    public static PGPSecretKey readSecretKey(InputStream in) throws IOException, PGPException {

        PGPSecretKeyRingCollection keyRingCollection = new PGPSecretKeyRingCollection(
                                                                                      org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in));

        //
        // We just loop through the collection till we find a key suitable for signing.
        // In the real world you would probably want to be a bit smarter about this.
        //
        PGPSecretKey secretKey = null;

        Iterator<PGPSecretKeyRing> rIt = keyRingCollection.getKeyRings();
        while (secretKey == null && rIt.hasNext()) {
            PGPSecretKeyRing keyRing = rIt.next();
            Iterator<PGPSecretKey> kIt = keyRing.getSecretKeys();
            while (secretKey == null && kIt.hasNext()) {
                PGPSecretKey key = kIt.next();
                if (key.isSigningKey()) {
                    secretKey = key;
                }
            }
        }

        // Validate secret key
        if (secretKey == null) {
            throw new IllegalArgumentException("Can't find private key in the key ring.");
        }
        if (!secretKey.isSigningKey()) {
            throw new IllegalArgumentException("Private key does not allow signing.");
        }
        if (secretKey.getPublicKey().isRevoked()) {
            throw new IllegalArgumentException("Private key has been revoked.");
        }
        if (!hasKeyFlags(secretKey.getPublicKey(), KeyFlags.SIGN_DATA)) {
            throw new IllegalArgumentException("Key cannot be used for signing.");
        }

        return secretKey;
    }
    
    
    @SuppressWarnings("rawtypes")
    private static boolean hasKeyFlags(PGPPublicKey encKey, int keyUsage) {
        if (encKey.isMasterKey()) {
            for (int i = 0; i != PGPUtil.MASTER_KEY_CERTIFICATION_TYPES.length; i++) {
                for (Iterator eIt = encKey.getSignaturesOfType(PGPUtil.MASTER_KEY_CERTIFICATION_TYPES[i]); eIt.hasNext();) {
                    PGPSignature sig = (PGPSignature) eIt.next();
                    if (!isMatchingUsage(sig, keyUsage)) {
                        return false;
                    }
                }
            }
        } else {
            for (Iterator eIt = encKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); eIt.hasNext();) {
                PGPSignature sig = (PGPSignature) eIt.next();
                if (!isMatchingUsage(sig, keyUsage)) {
                    return false;
                }
            }
        }
        return true;
    }
    
    
    private static boolean isMatchingUsage(PGPSignature sig, int keyUsage) {
        if (sig.hasSubpackets()) {
            PGPSignatureSubpacketVector sv = sig.getHashedSubPackets();
            if (sv.hasSubpacket(PGPUtil.KEY_FLAGS)) {
                if ((sv.getKeyFlags() & keyUsage) == 0) {
                    return false;
                }
            }
        }
        return true;
    }
    
    
    /**
     * Load a secret key ring collection from keyIn and find the secret key corresponding to keyID if it exists.
     * 
     * @param keyIn input stream representing a key ring collection.
     * @param keyID keyID we want.
     * @param pass passphrase to decrypt secret key with.
     * @return
     * @throws IOException
     * @throws PGPException
     * @throws NoSuchProviderException
     */
    private static PGPPrivateKey findSecretKey(InputStream keyIn, long keyID, char[] pass) throws IOException,
                                                                                          PGPException,
                                                                                          NoSuchProviderException {
        PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
                                                                           org.bouncycastle.openpgp.PGPUtil.getDecoderStream(keyIn));

        PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);

        if (pgpSecKey == null) {
            return null;
        }

        return pgpSecKey.extractPrivateKey(pass, "BC");
    }

    public static interface PGPPassSignatureChecker {

        public void check(PGPOnePassSignatureList signature);
    }

    
    /**
     * decrypt the passed in message stream
     */
    @SuppressWarnings("unchecked")
    public static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);

        PGPObjectFactory pgpF = new PGPObjectFactory(in);
        PGPEncryptedDataList enc;

        Object o = pgpF.nextObject();
        //
        // the first object might be a PGP marker packet.
        //
        if (o instanceof PGPEncryptedDataList) {
            enc = (PGPEncryptedDataList) o;
        } else {
            enc = (PGPEncryptedDataList) pgpF.nextObject();
        }

        //
        // find the secret key
        //
        Iterator<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
        PGPPrivateKey sKey = null;
        PGPPublicKeyEncryptedData pbe = null;

        while (sKey == null && it.hasNext()) {
            pbe = it.next();

            sKey = findSecretKey(keyIn, pbe.getKeyID(), passwd);
        }

        if (sKey == null) {
            throw new IllegalArgumentException("Secret key for message not found.");
        }

        InputStream clear = pbe.getDataStream(sKey, "BC");

        PGPObjectFactory plainFact = new PGPObjectFactory(clear);

        Object message = plainFact.nextObject();

        if (message instanceof PGPCompressedData) {
            PGPCompressedData cData = (PGPCompressedData) message;
            PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream());

            message = pgpFact.nextObject();
        }

        if (message instanceof PGPLiteralData) {
            PGPLiteralData ld = (PGPLiteralData) message;

            InputStream unc = ld.getInputStream();
            int ch;

            while ((ch = unc.read()) > 0) {
                out.write(ch);
            }
        } else if (message instanceof PGPOnePassSignatureList) {
            throw new PGPException("Encrypted message contains a signed message - not literal data.");
        } else {
            throw new PGPException("Message is not a simple encrypted file - type unknown.");
        }

        if (pbe.isIntegrityProtected()) {
            if (!pbe.verify()) {
                throw new PGPException("Message failed integrity check");
            }
        }
        
    }
    
    /**
     * 把PGP文件先用自己的私钥解密然后再用对方的公钥验证签名是否ok，然后解密出明文文件
     * @param in        需要解密的PGP文件流
     * @param out       解密后的明文文件
     * @param secretKey 自己的私钥（比如下载汇丰的PGP文件，这里就要用自己的私钥解密，这里是指阿里巴巴的私钥）
     * @param password  读取私钥的密码
     * @param publicKey 对方的公钥（比如下载汇丰的PGP文件，要用汇丰的公钥验证签名）
     * @throws Exception
     */
    public static void decryptVerifyFile(InputStream in, String decryptFile, String tempDecryptFile, InputStream secretKeyIn, String password, InputStream publicKeyIn) throws Exception {
        OutputStream tempOs = null;
        InputStream tempIs = null;

        Security.addProvider(new BouncyCastleProvider());

        in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);

        PGPObjectFactory pgpF = new PGPObjectFactory(in);
        PGPEncryptedDataList enc;

        Object o = pgpF.nextObject();
        //
        // the first object might be a PGP marker packet.
        //
        if (o instanceof PGPEncryptedDataList) {
            enc = (PGPEncryptedDataList) o;
        } else {
            enc = (PGPEncryptedDataList) pgpF.nextObject();
        }

        //
        // find the secret key
        //
        Iterator<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
        PGPPrivateKey sKey = null;
        PGPPublicKeyEncryptedData pbe = null;

        while (sKey == null && it.hasNext()) {
            pbe = it.next();

            sKey = findSecretKey(secretKeyIn, pbe.getKeyID(), password.toCharArray());
        }

        if (sKey == null) {
            throw new IllegalArgumentException("Secret key for message not found.");
        }

        PGPPublicKey publicKey = PGPUtil.readPublicKey(publicKeyIn);

        InputStream clear = ((PGPPublicKeyEncryptedData) enc.getEncryptedDataObjects().next()).getDataStream(sKey, "BC");
        PGPObjectFactory plainFact = new PGPObjectFactory(clear);
        Object message = null;

        PGPOnePassSignatureList onePassSignatureList = null;
        PGPSignatureList signatureList = null;
        PGPCompressedData compressedData = null;

        message = plainFact.nextObject();

        tempOs = new FileOutputStream(tempDecryptFile);
        while (message != null) {
//            System.out.println(message.toString());
            if (message instanceof PGPCompressedData) {
                compressedData = (PGPCompressedData) message;
                plainFact = new PGPObjectFactory(compressedData.getDataStream());
                message = plainFact.nextObject();
//                System.out.println(message.toString());
            }

            if (message instanceof PGPLiteralData) {
                Streams.pipeAll(((PGPLiteralData) message).getInputStream(), tempOs);
            } else if (message instanceof PGPOnePassSignatureList) {
                onePassSignatureList = (PGPOnePassSignatureList) message;
            } else if (message instanceof PGPSignatureList) {
                signatureList = (PGPSignatureList) message;
            } else {
                throw new PGPException("message unknown message type.");
            }
            message = plainFact.nextObject();
        }
        // 将信息装到临时文件中,因为还不知道这个文件是否签名通过，所以是临时文件
        tempOs.flush();
        tempOs.close();

        if (onePassSignatureList == null || signatureList == null) {
            throw new PGPException("Poor PGP. Signatures not found.");
        } else {

            for (int i = 0; i < onePassSignatureList.size(); i++) {
                PGPOnePassSignature ops = onePassSignatureList.get(0);
//                System.out.println("verifier : " + ops.getKeyID());
                logger.info("verifier : " + ops.getKeyID());
                if (publicKey != null) {
                    ops.initVerify(publicKey, "BC");
                    tempIs = new FileInputStream(tempDecryptFile);
                    int ch;
                    while ((ch = tempIs.read()) >= 0) {
                        ops.update((byte) ch);
                    }
                    tempIs.close();
                    PGPSignature signature = signatureList.get(i);
                    if (ops.verify(signature)) {
                        Iterator<?> userIds = publicKey.getUserIDs();
                        while (userIds.hasNext()) {
                            String userId = (String) userIds.next();
//                            System.out.println("Signed by " + userId);
                            logger.info("Signed by " + userId);
                        }
//                        System.out.println("Signature verified");
                        logger.info("Signature verified :" + decryptFile);
                    } else {
                        File tempFile = new File(tempDecryptFile);
                        tempFile.delete();
                        logger.error("Signature verification failed");
                        throw new SignatureException("Signature verification failed");
                    }
                }
            }
            // 文件签名验证通过，将临时文件变成正式文件
            File tempFile = new File(tempDecryptFile);
            File file = new File(decryptFile);
            if (file.exists()) {
                file.delete();
            }
            tempFile.renameTo(file);
        }

    }
    
    
    /**
     * 把PGP文件先用自己的私钥解密然后再用对方的公钥验证签名是否ok，然后解密出明文文件
     * @param in        需要解密的PGP文件流
     * @param out       解密后的明文文件
     * @param secretKey 自己的私钥（比如下载汇丰的PGP文件，这里就要用自己的私钥解密，这里是指阿里巴巴的私钥）
     * @param password  读取私钥的密码
     * @param publicKey 对方的公钥（比如下载汇丰的PGP文件，要用汇丰的公钥验证签名）
     * @throws Exception
     */
    public static void decryptVerifyFile(InputStream in, String decryptFile, String tempDecryptFile, PGPSecretKey secretKey, String password, PGPPublicKey publicKey) throws Exception {
            OutputStream tempOs = null;
            InputStream  tempIs = null;

            tempOs = new FileOutputStream(tempDecryptFile);
            
            Provider provider = new BouncyCastleProvider();
            Security.addProvider(provider);
            in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);

            PGPObjectFactory pgpF = new PGPObjectFactory(in);
            PGPEncryptedDataList enc = (PGPEncryptedDataList) pgpF.nextObject();

            PGPPrivateKey privateKey = secretKey.extractPrivateKey(password.toCharArray(), provider);
            InputStream clear = ((PGPPublicKeyEncryptedData) enc.getEncryptedDataObjects().next()).getDataStream(privateKey, "BC");
            PGPObjectFactory plainFact = new PGPObjectFactory(clear);
            
            Object message = null;

            PGPOnePassSignatureList onePassSignatureList = null;
            PGPSignatureList signatureList = null;
            PGPCompressedData compressedData = null;

            message = plainFact.nextObject();

            while (message != null) {
//                System.out.println(message.toString());
                if (message instanceof PGPCompressedData) {
                    compressedData = (PGPCompressedData) message;
                    plainFact = new PGPObjectFactory(compressedData.getDataStream());
                    message = plainFact.nextObject();
//                    System.out.println(message.toString());
                }

                if (message instanceof PGPLiteralData) {
                    Streams.pipeAll(((PGPLiteralData) message).getInputStream(), tempOs);
                } else if (message instanceof PGPOnePassSignatureList) {
                    onePassSignatureList = (PGPOnePassSignatureList) message;
                } else if (message instanceof PGPSignatureList) {
                    signatureList = (PGPSignatureList) message;
                } else {
                    throw new PGPException("message unknown message type.");
                }
                message = plainFact.nextObject();
            }
            //将信息装到临时文件中,因为还不知道这个文件是否签名通过，所以是临时文件
            tempOs.flush();
            tempOs.close();
            
            if (onePassSignatureList == null || signatureList == null) {
                throw new PGPException("Poor PGP. Signatures not found.");
            } else {

                for (int i = 0; i < onePassSignatureList.size(); i++) {
                    PGPOnePassSignature ops = onePassSignatureList.get(0);
//                    System.out.println("verifier : " + ops.getKeyID());
                    logger.info("verifier : " + ops.getKeyID());
                    if (publicKey != null) {
                        ops.initVerify(publicKey, "BC");
                        tempIs = new FileInputStream(tempDecryptFile);
                        int ch;
                        while ((ch = tempIs.read()) >= 0) {
                            ops.update((byte) ch);
                        }   
                        tempIs.close();
                        PGPSignature signature = signatureList.get(i);
                        if (ops.verify(signature)) {
                            Iterator<?> userIds = publicKey.getUserIDs();
                            while (userIds.hasNext()) {
                                String userId = (String) userIds.next();
//                                System.out.println("Signed by " + userId);
                                logger.info("Signed by " + userId);
                            }
//                            System.out.println("Signature verified");
                            logger.info("Signature verified");
                        } else {
                            File tempFile = new File(tempDecryptFile);
                            tempFile.delete();
                            throw new SignatureException("Signature verification failed");
                        }
                    }
                }
                //文件签名验证通过，将临时文件变成正式文件
                File tempFile = new File(tempDecryptFile);
                File file = new File(decryptFile);
                if (file.exists()) {
                    file.delete();
                }
                tempFile.renameTo(file);
            }

    }
    
    public static InputStream decryptFile(InputStream fileToDecrypt, InputStream keyIn, char[] passwd) throws Exception {

        return decryptFile(fileToDecrypt, keyIn, passwd, new PGPPassSignatureChecker() {

            @Override
            public void check(PGPOnePassSignatureList signature) {
            }
        });
    }

    public static File decryptFile(InputStream fileToDecrypt, InputStream keyIn, char[] passwd, String destFile)
                                                                                                                throws Exception {
        return decryptFile(fileToDecrypt, keyIn, passwd, null, destFile);
    }

    public static File decryptFile(InputStream fileToDecrypt, InputStream keyIn, char[] passwd,
                                   PGPPassSignatureChecker checker, String destFile) throws Exception {
        InputStream input = null;
        DataOutputStream output = null;
        try {
            input = new BufferedInputStream(decryptFile(fileToDecrypt, keyIn, passwd, checker));
            File dest = new File(destFile);
            output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
            byte[] b = new byte[1024];
            int byteRead;
            while ((byteRead = input.read(b)) != -1) {
                output.write(b, 0, byteRead);
            }
            return dest;
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
            } catch (IOException e) {
                logger.error("Close input stream failure", e);
            }
            try {
                if (output != null) {
                    output.close();
                }
            } catch (IOException e) {
                logger.error("Close output stream failure", e);
            }

        }
    }

    /**
     * decrypt the passed in message stream
     */
    @SuppressWarnings("unchecked")
    public static InputStream decryptFile(InputStream fileToDecrypt, InputStream keyIn, char[] passwd,
                                          PGPPassSignatureChecker checker) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        fileToDecrypt = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(fileToDecrypt);

        PGPObjectFactory pgpF = new PGPObjectFactory(fileToDecrypt);
        PGPEncryptedDataList enc;

        Object o = pgpF.nextObject();
        //
        // the first object might be a PGP marker packet.
        //
        if (o instanceof PGPEncryptedDataList) {
            enc = (PGPEncryptedDataList) o;
        } else {
            enc = (PGPEncryptedDataList) pgpF.nextObject();
        }

        //
        // find the secret key
        //
        Iterator<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
        PGPPrivateKey sKey = null;
        PGPPublicKeyEncryptedData pbe = null;
        while (sKey == null && it.hasNext()) {
            pbe = it.next();

            sKey = findSecretKey(keyIn, pbe.getKeyID(), passwd);
        }

        if (sKey == null) {
            throw new IllegalArgumentException("Secret key for message not found.");
        }
        InputStream clear = pbe.getDataStream(sKey, "BC");

        PGPObjectFactory plainFact = new PGPObjectFactory(clear);

        Object message = plainFact.nextObject();

        if (message instanceof PGPCompressedData) {
            PGPCompressedData cData = (PGPCompressedData) message;
            PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream());

            message = pgpFact.nextObject();
            // 如果第一部分是签名信息，则根据传进来的校验器，校验
            if (message instanceof PGPOnePassSignatureList) {
                if (checker != null) {
                    checker.check((PGPOnePassSignatureList) message);
                }
                message = pgpFact.nextObject();
            }
        }
        if (message instanceof PGPLiteralData) {
            PGPLiteralData ld = (PGPLiteralData) message;

            return ld.getInputStream();
        } else if (message instanceof PGPOnePassSignatureList) {
            throw new PGPException("Encrypted message contains a signed message - not literal data.");
        } else {
            throw new PGPException("Message is not a simple encrypted file - type unknown.");
        }

        // if (pbe.isIntegrityProtected()) {
        // if (!pbe.verify()) {
        // throw new PGPException("Message failed integrity check");
        // }
        // }
    }

    
    /**
     * 加密文件（此方法不要使用，暂时也没有使用到，如果要使用需要改造，因为这里用到了ByteArrayOutputStream这个类
     * 当XML文件很大的时候会导致JVM内存溢出甚至直接将JVM进程搞挂）
     * @param out
     * @param fileName
     * @param encKey
     * @param armor
     * @param withIntegrityCheck
     * @throws IOException
     * @throws NoSuchProviderException
     * @throws PGPException
     */
    public static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey, boolean armor,
                                   boolean withIntegrityCheck) throws IOException, NoSuchProviderException,
                                                              PGPException {
        PGPCompressedDataGenerator comData = null;
        OutputStream cOut = null;
        try {
            
            Security.addProvider(new BouncyCastleProvider());
            
            if (armor) {
                out = new ArmoredOutputStream(out);
            }
            
            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
            
            comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
            
            org.bouncycastle.openpgp.PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY,
                                                                    new File(fileName));    
            
            PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, withIntegrityCheck,
                                                                          new SecureRandom(), "BC");
            
            cPk.addMethod(encKey);
            
            byte[] bytes = bOut.toByteArray();
            
            cOut = cPk.open(out, bytes.length);
            
            cOut.write(bytes);
        } finally{
            if (comData!=null) {  
                comData.close();          
            }
            if (cOut!=null) {  
                cOut.close();
            }
            if (out!=null) {   
                out.close();
            }
        }
    }

    
    /**
     * 把文件用自己的私钥签名，然后用对方的公钥加密并重新生成文件
     * @param out       签名加密后的PGP文件的输出流
     * @param fileName  原始文件路径（xml）
     * @param publicKey 加密的公钥（一般是对方的公钥，比如汇丰银行的公钥）
     * @param secretKey 自己的私钥（一般是阿里自己的私钥）
     * @param password  解密私钥的PASSWORD（读取自己的私钥需要的密码，无密码无法读取自己的这个私钥）
     * @param armor     一般设置成false
     * @param withIntegrityCheck 一般设置成true
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static void signEncryptFile(OutputStream out, String fileName, PGPPublicKey publicKey,
                                       PGPSecretKey secretKey, String password, boolean armor,
                                       boolean withIntegrityCheck) throws Exception {

        // Initialize Bouncy Castle security provider
        Provider provider = new BouncyCastleProvider();
        Security.addProvider(provider);

        if (armor) {
            out = new ArmoredOutputStream(out);
        }

        // Initialize encrypted data generator
        PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(
                                                                                         SymmetricKeyAlgorithmTags.TRIPLE_DES,
                                                                                         withIntegrityCheck,
                                                                                         new SecureRandom(), provider);
        encryptedDataGenerator.addMethod(publicKey);
        OutputStream encryptedOut = encryptedDataGenerator.open(out, new byte[BUFFER_SIZE]);

        // Initialize compressed data generator
        PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
        OutputStream compressedOut = compressedDataGenerator.open(encryptedOut, new byte[BUFFER_SIZE]);

        // Initialize signature generator
        PGPPrivateKey privateKey = secretKey.extractPrivateKey(password.toCharArray(), provider);
        PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(),
                                                                             HashAlgorithmTags.SHA1, provider);
        signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, privateKey);

        boolean firstTime = true;
        Iterator<String> it = secretKey.getPublicKey().getUserIDs();
        while (it.hasNext() && firstTime) {
            PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
            spGen.setSignerUserID(false, (String) it.next());
            signatureGenerator.setHashedSubpackets(spGen.generate());
            // Exit the loop after the first iteration
            firstTime = false;
        }
        signatureGenerator.generateOnePassVersion(false).encode(compressedOut);

        // Initialize literal data generator
        PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
        OutputStream literalOut = literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY, fileName, new Date(),
                                                            new byte[BUFFER_SIZE]);

        // Main loop - read the "in" stream, compress, encrypt and write to the "out" stream
        FileInputStream in = new FileInputStream(fileName);
        byte[] buf = new byte[BUFFER_SIZE];
        int len;
        while ((len = in.read(buf)) != -1) {
            literalOut.write(buf, 0, len);
            signatureGenerator.update(buf, 0, len);
        }

        in.close();
        literalDataGenerator.close();
        // Generate the signature, compress, encrypt and write to the "out" stream
        signatureGenerator.generate().encode(compressedOut);
        compressedDataGenerator.close();
        encryptedDataGenerator.close();
        if (armor) {
            out.close();
        }
    }
    
    
    
    public static boolean verifyFile(InputStream in, InputStream keyIn, String extractContentFile) throws Exception {
        in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);

        PGPObjectFactory pgpFact = new PGPObjectFactory(in);
        PGPCompressedData c1 = (PGPCompressedData) pgpFact.nextObject();

        pgpFact = new PGPObjectFactory(c1.getDataStream());

        PGPOnePassSignatureList p1 = (PGPOnePassSignatureList) pgpFact.nextObject();

        PGPOnePassSignature ops = p1.get(0);

        PGPLiteralData p2 = (PGPLiteralData) pgpFact.nextObject();

        InputStream dIn = p2.getInputStream();

        IOUtils.copy(dIn, new FileOutputStream(extractContentFile));

        int ch;
        PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(org.bouncycastle.openpgp.PGPUtil.getDecoderStream(keyIn));

        PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID());

        FileOutputStream out = new FileOutputStream(p2.getFileName());

//      ops.init(new BcPGPContentVerifierBuilderProvider(), key);
        ops.initVerify(key, "BC");
        while ((ch = dIn.read()) >= 0) {
            ops.update((byte) ch);
            out.write(ch);
        }

        out.close();

        PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();
        return ops.verify(p3.get(0));
    }
    
    
}
